package yaml

import (
	"errors"
	"gitee.com/dennis-kk/service-box-go/util/config/reader"
	"gitee.com/dennis-kk/service-box-go/util/config/source"
	"github.com/imdario/mergo"
	"gopkg.in/yaml.v3"
	"time"
)

type (
	yamlReader struct {
		opts reader.Options
	}
)

//Merge will foreach all change set, and merge keys
func (m *yamlReader) Merge(changes ...*source.ChangeSet) (*source.ChangeSet, error) {
	var merged map[string]interface{}

	for _, change := range changes {
		if m == nil {
			continue
		}

		if len(change.Data) == 0 {
			continue
		}

		codec, ok := m.opts.Encoding[change.Format]
		if !ok {
			// fallback
			return nil, errors.New("unsupported format")
		}

		var data map[string]interface{}
		if err := codec.Decode(change.Data, &data); err != nil {
			return nil, err
		}
		if err := mergo.Map(&merged, data, mergo.WithOverride); err != nil {
			return nil, err
		}
	}
	sumBinary, err := yaml.Marshal(&merged)
	if err != nil {
		return nil, err
	}

	cs := &source.ChangeSet{
		Timestamp: time.Now(),
		Data:      sumBinary,
		Source:    "yaml",
		Format:    "yaml",
	}
	cs.Checksum = cs.Sum()

	return cs, nil
}

func (m *yamlReader) Values(ch *source.ChangeSet) (reader.Values, error) {
	if ch == nil {
		return nil, errors.New("changeset is nil")
	}
	if ch.Format != "yaml" {
		return nil, errors.New("unsupported format")
	}
	return newValues(ch)
}

func (m *yamlReader) String() string {
	return "yaml"
}

// NewReader creates a yaml reader read all config to yaml
func NewReader(opts ...reader.Option) reader.Reader {
	options := reader.NewOptions(opts...)
	return &yamlReader{
		opts: options,
	}
}
